package com.yunhe.authority.service.authority.impl;

import com.yunhe.authority.domain.authority.Menu;
import com.yunhe.authority.domain.authority.MenuUnBindStationVo;
import com.yunhe.authority.domain.authority.Role;
import com.yunhe.authority.repository.authority.MenuRepository;
import com.yunhe.authority.service.authority.FirmService;
import com.yunhe.authority.service.authority.MenuService;
import com.yunhe.authority.service.authority.RoleService;
import com.yunhe.common.exception.ArgumentErrorException;
import com.yunhe.common.exception.ObjectNotFoundException;
import com.yunhe.common.exception.UniqueConstraintsException;
import com.yunhe.common.filter.ServerContextHolder;
import com.yunhe.common.util.StringUtil;
import com.yunhe.common.util.CollectionUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

import static java.util.stream.Collectors.toList;

/**
 * 菜单Service
 * @author liuronglei
 */
@Service
public class MenuServiceImpl implements MenuService {
    @Autowired
    MenuRepository menuRepository;
    @Autowired
    FirmService firmService;
    @Autowired
    RoleService roleService;

    /**
     * 菜单对象处理和判断
     * @param menu 菜单对象
     */
    private void processBeforeSave(Menu menu)
            throws ArgumentErrorException, UniqueConstraintsException {
        //判断菜单名、标题和Url是否为空
        if (StringUtil.isEmpty(menu.getName())
                || StringUtil.isEmpty(menu.getTitle())) {
            throw new ArgumentErrorException("菜单名和标题不能为空");
        }
        //判断菜单名是否已存在
        Menu menuByName = this.findByName(menu.getName());
        if (menuByName != null) {
            if (menu.getId() == null || menuByName.getId().longValue() != menu.getId().longValue()) {
                throw new UniqueConstraintsException("该菜单名已存在");
            }
        }
    }

    /**
     * 更新父菜单关系（添加到父菜单）
     * @param menu 子菜单对象
     * @param parentId 父菜单ID
     */
    private void updateParent(Menu menu, Long parentId) throws ObjectNotFoundException {
        Menu parentMenu = findByMenuId(parentId);
        if (parentMenu == null) {
            throw new ObjectNotFoundException("找不到ID为" + parentId + "的父菜单");
        }
        menu.setParentId(parentId);
        List<Menu> children = parentMenu.getChildren();
        if (children == null) {
            children = new ArrayList<>();
            parentMenu.setChildren(children);
        }
        children.add(menu);
        menuRepository.save(parentMenu);
    }

    /**
     * 对菜单列表进行排序
     * @param menuList 菜单列表
     */
    private void sortMenus(List<Menu> menuList) {
        if (menuList != null && menuList.size() > 0) {
            Collections.sort(menuList);
            for (Menu menu : menuList) {
                if (menu.getChildren() != null && menu.getChildren().size() > 0) {
                    sortMenus(menu.getChildren());
                }
            }
        }
    }

    /**
     * 为用户菜单列表构建树形
     * @param resultMap 要构建树形的菜单Map
     * @param allMenu 全部菜单列表
     */
    private void buildMenuTreeForUser(Map<Long, Menu> resultMap, List<Menu> allMenu) {
        for (int i = allMenu.size() - 1; i >= 0; i--) {
            if (!resultMap.containsKey(allMenu.get(i).getId())) {
                allMenu.remove(i);
            } else {
                if (allMenu.get(i).getChildren() != null) {
                    buildMenuTreeForUser(resultMap, allMenu.get(i).getChildren());
                }
            }
        }
    }

    /**
     * 更新父菜单关系
     * @param childId 子菜单ID
     * @param parentId 父菜单ID
     */
    private void updateParentRelation(Long childId, Long parentId) throws ObjectNotFoundException {
        if (parentId != null) {
            Menu parentMenu = findByMenuId(parentId);
            if (parentMenu == null) {
                throw new ObjectNotFoundException("找不到ID为" + parentId + "的父菜单");
            }
            //先删除原父子关系
            menuRepository.delChildRelation(childId);
            //再添加新父子关系
            menuRepository.addChildRelation(parentId, childId);
        } else {
            menuRepository.delChildRelation(childId);
        }
    }

    /**
     * 更新父菜单关系
     * @param childId 子菜单ID
     * @param parentId 父菜单ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class, transactionManager = "transactionManager")
    public void updateParent(Long childId, Long parentId) throws ObjectNotFoundException {
        Menu menu = this.findByMenuId(childId);
        if (menu == null) {
            throw new ObjectNotFoundException("找不到ID为" + childId + "的菜单");
        }
        //更新父菜单关系
        updateParentRelation(childId, parentId);
        //如果有需要，则更新菜单root属性
        if (("0".equals(menu.getRoot()) && parentId != null) || ("1".equals(menu.getRoot()) && parentId == null)) {
            menu.setRoot("0");
            menuRepository.save(menu);
        }
    }

    /**
     * 新增菜单对象
     * @param menu 菜单对象
     * @param parentId 父菜单ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class, transactionManager = "transactionManager")
    public Menu createMenu(Menu menu, Long parentId)
            throws ArgumentErrorException, UniqueConstraintsException, ObjectNotFoundException {
        menu.setId(null);
        menu.setParentId(parentId);
        menu.setSn(String.valueOf(System.currentTimeMillis()));
        menu.setTopTime(String.valueOf(System.currentTimeMillis()));
        processBeforeSave(menu);
        if (parentId != null) {
            menu.setRoot("0");
        }
        //新增菜单
        Menu newMenu = menuRepository.save(menu);
        //绑定父菜单关系
        if (parentId != null) {
            updateParent(newMenu.getId(), parentId);
        }
        //平台管理员绑定菜单
        Role root = roleService.findByName("superRoot");
        try {
            roleService.bindMenu(root.getId(), newMenu.getId());
//            roleService.bindHasMenu(root.getId(), newMenu.getId());
        } catch (Exception e) {}
        return newMenu;
    }

    /**
     * 删除菜单对象
     * @param menuId 菜单对象ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class, transactionManager = "transactionManager")
    public void deleteMenu(Long menuId) {
        menuRepository.deleteById(menuId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class, transactionManager = "transactionManager")
    public void deleteMenuByIds(String menuIds) {
        String[] menuIdList = menuIds.split(",");
        for (String menuId : menuIdList) {
            //查询子集然后删除
            List<Menu> menuChildList = (List<Menu>)menuRepository.findChildren(Long.valueOf(menuId));
            if (CollectionUtil.isNotEmpty(menuChildList)) {
                menuChildList.forEach(item -> {
                    this.deleteMenu(item.getId());
                });
            }
            this.deleteMenu(Long.valueOf(menuId));
        }
    }

    /**
     * 更新菜单对象（更新全部字段）
     * @param menu 菜单对象
     * @param parentId 父菜单ID
     */
    @Override
    @Transactional(rollbackFor = Exception.class, transactionManager = "transactionManager")
    public Menu updateMenu(Menu menu, Long parentId)
            throws ArgumentErrorException, UniqueConstraintsException, ObjectNotFoundException {
        Optional<Menu> existMenu = menuRepository.findById(menu.getId());
        if (!existMenu.isPresent()) {
            throw new ObjectNotFoundException("找不到ID为" + menu.getId() + "的菜单");
        }
        menu.setParentId(parentId);
        processBeforeSave(menu);

        Menu oldMenu = existMenu.get();
        menu.setTopTime(oldMenu.getTopTime());
        menu.setSn(oldMenu.getSn());

        Menu newMenu = menuRepository.save(menu);
        //绑定父菜单关系
        updateParent(menu.getId(), parentId);

        return newMenu;
    }

    /**
     * 更新菜单对象（更新指定字段）
     * @param menuId 菜单ID
     * @param parentId 父菜单ID
     * @param name 菜单名
     * @param title 菜单标题
     * @param url 菜单Url
     * @param icon 图标
     * @param activity 是否可用
     * @param sn 排序
     */
    @Override
    @Transactional(rollbackFor = Exception.class, transactionManager = "transactionManager")
    public Menu updateMenu(Long menuId,
                           Long parentId,
                           String name,
                           String title,
                           String url,
                           String icon,
                           String activity,
                           String root,
                           String sn)
            throws ArgumentErrorException, ObjectNotFoundException, UniqueConstraintsException {
        Menu menu = this.findByMenuId(menuId);
        if (menu == null) {
            throw new ObjectNotFoundException("找不到ID为" + menuId + "的菜单");
        }
        Optional.ofNullable(name).ifPresent(menu::setName);
        Optional.ofNullable(title).ifPresent(menu::setTitle);
        Optional.ofNullable(url).ifPresent(menu::setUrl);
        Optional.ofNullable(icon).ifPresent(menu::setIcon);
        Optional.ofNullable(activity).ifPresent(menu::setActivity);
        Optional.ofNullable(root).ifPresent(menu::setRoot);
        Optional.ofNullable(sn).ifPresent(menu::setSn);
        processBeforeSave(menu);
        menu.setParentId(parentId);
        Menu newMenu = menuRepository.save(menu);
        //绑定父菜单关系
        if (parentId != null) {
            updateParent(newMenu, parentId);
        }
        //updateParentMenu(menuId, menu.getParentId(), parentId.isPresent() ? parentId.get() : null);
        return newMenu;
    }

    /**
     * 根据菜单ID获得菜单对象
     * @param id 菜单ID
     */
    @Override
    public Menu findByMenuId(Long id) {
        return menuRepository.findById(id).orElse(null);
    }

    /**
     * 根据菜单编号获得菜单对象
     * @param name 菜单编号
     */
    @Override
    public Menu findByName(String name) {
        return menuRepository.findByName(name);
    }

    /**
     * 获得所有菜单对象列表
     */
    @Override
    public Iterable<Menu> findMenus() {
        //如果深度>1，效率比较低，如果要支撑上3级菜单，需要修改该方法
        //如需重新启动，需要在Menu.java的children属性上添加@Relationship(type = "HAS_CHILD")注解
        List<Menu> results = (List<Menu>)menuRepository.findAll();
        //进行排序
        sortMenus(results);
        setChildren(results);
        return results;
    }

    @Override
    public Iterable<Menu> findMenusByStationIdOrFirmId(Long stationId, Long firmId) throws ArgumentErrorException {
        List<Menu> results = new ArrayList<>();
        List<Menu> allMenu = (List<Menu>)menuRepository.findAll();

        if (stationId == null && firmId == null) {
            throw new ArgumentErrorException("请选择单位或者园区");
        }
        if (stationId != null) {
            results = (List<Menu>)menuRepository.findMenusByStationId(stationId);
        }
        if (firmId != null) {
            results = (List<Menu>)menuRepository.findMenusByFirmId(firmId);
        }

        results.forEach(menu -> {
            if (allMenu.contains(menu)) {
                menu.setEnable(true);
            }
        });

        return setChildren(allMenu);
    }

    @Override
    public Iterable<Menu> findMenusByStationIdOrFirmIdAndRoleId(Long stationId, Long firmId, Long roleId) throws ArgumentErrorException {
        if (roleId == null) {
            throw new ArgumentErrorException("请选择角色");
        }

        List<Menu> results = new ArrayList<>();
        if (stationId == null && firmId == null) {
            results = (List<Menu>)menuRepository.findAll();
        }

        if (stationId != null) {
            results = (List<Menu>)menuRepository.findMenusByStationId(stationId);
        }
        if (firmId != null) {
            results = (List<Menu>)menuRepository.findMenusByFirmId(firmId);
        }

        List<Menu> roleMenuIterable = (List<Menu>)menuRepository.findMenusByRoleId(roleId);
        results.forEach(menu -> {
            if (roleMenuIterable.contains(menu)) {
                menu.setEnable(true);
            }
        });

        return setChildren(results);
    }

    //查询子集
    private void searchChildren(List<Menu> menuList) {
        if (CollectionUtil.isNotEmpty(menuList)) {
            menuList.forEach(menu -> {
                List<Menu> menuChildrenList = (List<Menu>) menuRepository.findChildren(menu.getId());
                if (CollectionUtil.isNotEmpty(menuChildrenList)) {
                    menu.setChildren(menuChildrenList);
                    //再查一遍子集的子集
                    searchChildren(menuChildrenList);
                }
            });
        }
    }

    //设置子集
    private List<Menu> setChildren(List<Menu> menuList) {
        if (CollectionUtil.isNotEmpty(menuList)) {
            for (Menu parentMenu : menuList) {
                for (Menu childrenMenu : menuList) {
                    if (childrenMenu.getParentId() != null &&
                            parentMenu.getId().equals(childrenMenu.getParentId())) {
                        if (CollectionUtil.isEmpty(parentMenu.getChildren())) {
                            ArrayList<Menu> menus = new ArrayList<>();
                            menus.add(childrenMenu);
                            parentMenu.setChildren(menus);
                        } else {
                            parentMenu.getChildren().add(childrenMenu);
                        }
                    }
                }
            }
        }
        menuList.removeIf(menu -> menu.getParentId() != null);
        return menuList;
    }

    /**
     * 根据用户ID获得菜单对象列表
     * @param userId 用户ID
     * @param firmCode 单位代码
     */
    @Override
    public Iterable<Menu> findMenusByUserId(Long firmCode, Long userId) {
        List<Menu> results = (List<Menu>)menuRepository.findMenusByUserId(firmCode, userId);
        if (results != null) {
            Map<Long, Menu> resultMap = new HashMap<>();
            for (Menu menu : results) {
                resultMap.put(menu.getId(), null);
            }
            results = (List<Menu>)findMenus();
            buildMenuTreeForUser(resultMap, results);
        }
        sortMenus(results);
        return results;
    }

    /**
     * 根据用户ID获得菜单对象列表
     * @param userId 用户ID
     * @param firmCode 单位代码
     */
    @Override
    public Iterable<Menu> findAppMenusByUserId(Long firmCode, Long userId) {
        List<Menu> results = (List<Menu>)menuRepository.findAppMenusByUserId(firmCode, userId);
        if (results != null) {
            Map<Long, Menu> resultMap = new HashMap<>();
            for (Menu menu : results) {
                resultMap.put(menu.getId(), null);
            }
            results = (List<Menu>)findMenus();
            buildMenuTreeForUser(resultMap, results);
        }
        sortMenus(results);
        return results;
    }

    @Override
    @Transactional(rollbackFor = Exception.class, transactionManager = "transactionManager")
    public void menusBindStationOrFirm(MenuUnBindStationVo menuUnBindStationVo) throws ObjectNotFoundException, ArgumentErrorException {
        Long stationId = menuUnBindStationVo.getStationId();
        //如果项目ID不为空
        if (stationId != null) {
            //解绑前先查询园区角色是否绑定了菜单
            /*if (StringUtils.isNotEmpty(ids)){
                String[] idList = ids.split(",");
                for (String id : idList) {
                    List<Role> roleByStation = menuRepository.getRoleByStation(Long.valueOf(id), stationId);
                    if (!roleByStation.isEmpty()){
                        throw new ArgumentErrorException("该园区下的角色还存在菜单权限，请先取消角色的菜单权限再进行操作");
                    }
                }
            }*/
            //先解绑
            menuRepository.menusUnbindByStationId(stationId,menuUnBindStationVo.getSystemMenuCategory());
            if (CollectionUtil.isNotEmpty(menuUnBindStationVo.getExistMenuId())) {
                for (Long id : menuUnBindStationVo.getExistMenuId()) {
                    Optional<Menu> menu = menuRepository.findById(id);
                    if (menu.isPresent()) {
                        menuRepository.menusBindByStationId(id, stationId);
                    }
                }
            }
            if (CollectionUtil.isNotEmpty(menuUnBindStationVo.getNotExistMenuId())) {
                for (Long id : menuUnBindStationVo.getNotExistMenuId()) {
                    menuRepository.menusUnBindByStationIdAndMenuId(stationId,id);
                }
            }
        }

        //TODO 单位绑定逻辑待写
    }

    @Override
    public List<Menu> findMenusByRoleId(String roleIds) {
        String[] roleIdsSplit = roleIds.split(",");
        List<Menu> menuIterable = new ArrayList<>();
        for (String roleId : roleIdsSplit) {
            List<Menu> newMenuIterable = (List<Menu>)menuRepository.findMenusByRoleId(Long.valueOf(roleId));
            menuIterable = takeUnion(menuIterable, newMenuIterable);
        }
        setChildren(menuIterable);
        //根据topTime从大到小
        menuIterable.sort((o1, o2) -> Integer.compare(0, o1.getTopTime().compareTo(o2.getTopTime())));
        return menuIterable;
    }

    @Override
    public List<Menu> findMenusBySystem(String systemMenuCategory) {
        List<Menu> menuList = new ArrayList<>();
        Iterable<Menu> menus = menuRepository.findBySystemMenuCategory(systemMenuCategory);
        if (menus.iterator().hasNext()) {
            menuList = (List<Menu>)menus;
            setChildren(menuList);
        }
        return menuList;
    }

    @Override
    public List<Menu> findMenusByCompanyId(Long companyId) {
        List<Menu> stationMenus = new ArrayList<>();
        String stationId = ServerContextHolder.getContext().getProjectCode();
        if (StringUtils.isNotEmpty(stationId)) {
            stationMenus = (List<Menu>)menuRepository.findMenusByStationId(Long.valueOf(stationId));

            List<Menu> companyMenus = (List<Menu>)menuRepository.findMenusByCompanyId(companyId);
            stationMenus.forEach(menu -> {
                if (companyMenus.contains(menu)) {
                    menu.setEnable(true);
                }
            });
        }

        return setChildren(stationMenus);
    }

    @Override
    public List<Menu> findMenusByCompanyIdAndRoleId(Long companyId, Long roleId) throws ArgumentErrorException {

        if (companyId == null || roleId == null) {
            throw new ArgumentErrorException("请选择角色或者企业");
        }

        List<Menu> companyMenus = (List<Menu>) menuRepository.findMenusByCompanyId(companyId);
        List<Menu> roleMenus = (List<Menu>) menuRepository.findMenusByRoleId(roleId);

        companyMenus.forEach(menu -> {
            if (roleMenus.contains(menu)) {
                menu.setEnable(true);
            }
        });

        return setChildren(companyMenus);
    }

    @Override
    public void menusBindCompany(String ids, Long companyId, String systemMenuCategory) {
        //这边先解绑  然后再绑定
        menuRepository.menusUnbindCompany(companyId, systemMenuCategory);
        if (StringUtils.isNotEmpty(ids)) {
            String[] idsStr = ids.split(",");
            for (String id : idsStr) {
                menuRepository.menusBindCompany(companyId, Long.valueOf(id));
            }
        }
    }

    @Override
    public void topMenu(Long id) {
        Optional<Menu> optionalMenu = menuRepository.findById(id);
        if (optionalMenu.isPresent()) {
            Menu menu = optionalMenu.get();
            menu.setTopTime(String.valueOf(System.currentTimeMillis()));
            menuRepository.save(menu);
        }
    }

    /**
     * 取权限的去重并集
     * @param menuIterable
     * @param newMenuIterable
     * @return
     */
    private List<Menu> takeUnion(List<Menu> menuIterable, List<Menu> newMenuIterable) {
        List<Menu> menuList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(menuIterable) && CollectionUtil.isNotEmpty(newMenuIterable)) {
            menuList.addAll(menuIterable);
            menuList.addAll(newMenuIterable);
            menuList = menuList.stream().distinct().collect(toList());
        } else if (CollectionUtil.isEmpty(menuIterable) && CollectionUtil.isNotEmpty(newMenuIterable)) {
            menuList = newMenuIterable;
        } else {
            menuList = menuIterable;
        }
        return menuList;
    }
}
